#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <pthread.h>
#include <openssl/md5.h>
#include <sys/time.h>
#include <errno.h>

#define THREAD_COUNT 4
#define CHECKSUM_SIZE 4  // 校验和大小，这里使用 4 字节

typedef struct {
    int sockfd;
    char *data;
    size_t size;
    long offset;
    int thread_id;
} ThreadData;

// 定义互斥锁和条件变量
pthread_mutex_t send_mutex;
pthread_cond_t cond_var;
int current_thread = 0;

// 计算校验和
unsigned int calculate_checksum(const char *data, size_t size) {
    unsigned int checksum = 0;
    for (size_t i = 0; i < size; i++) {
        checksum += (unsigned char)data[i];
    }
    return checksum;
}

// 安全的发送函数，确保数据完整发送
ssize_t safe_send(int sockfd, const void *buf, size_t len, int flags) {
    size_t total_sent = 0;
    while (total_sent < len) {
        ssize_t sent = send(sockfd, (const char *)buf + total_sent, len - total_sent, flags);
        if (sent < 0) {
            if (errno == EINTR) {
                continue; // 处理被信号中断的情况
            }
            return -1;
        }
        total_sent += sent;
    }
    return total_sent;
}

void *send_data(void *arg) {
    ThreadData *thread_data = (ThreadData *)arg;
    size_t total_sent = 0;

    // 计算校验和
    unsigned int checksum = calculate_checksum(thread_data->data, thread_data->size);

    // 等待轮到自己执行
    pthread_mutex_lock(&send_mutex);
    while (thread_data->thread_id != current_thread) {
        pthread_cond_wait(&cond_var, &send_mutex);
    }

    // 发送线程 ID
    if (safe_send(thread_data->sockfd, &(thread_data->thread_id), sizeof(int), 0) < 0) {
        perror("send thread ID failed");
        pthread_mutex_unlock(&send_mutex);
        pthread_exit(NULL);
    }

    // 发送校验和
    if (safe_send(thread_data->sockfd, &checksum, CHECKSUM_SIZE, 0) < 0) {
        perror("send checksum failed");
        pthread_mutex_unlock(&send_mutex);
        pthread_exit(NULL);
    }

    printf("Thread %d started. Data pointer: %p, Size: %zu, Checksum: %u\n", thread_data->thread_id, thread_data->data, thread_data->size, checksum);

    while (total_sent < thread_data->size) {
        ssize_t sent_bytes = safe_send(thread_data->sockfd, thread_data->data + total_sent, thread_data->size - total_sent, 0);
        if (sent_bytes < 0) {
            perror("send failed");
            pthread_mutex_unlock(&send_mutex);
            pthread_exit(NULL);
        }
        total_sent += sent_bytes;

        // 打印进度
        printf("Thread %d: Sent %zu bytes (Progress: %.2f%%)\n", 
               thread_data->thread_id, total_sent, 
               (double)(total_sent) / thread_data->size * 100);
    }
    printf("Thread %d finished sending.\n", thread_data->thread_id); 

    // 通知下一个线程
    current_thread++;
    pthread_cond_broadcast(&cond_var);
    pthread_mutex_unlock(&send_mutex);

    pthread_exit(NULL);
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <filename> <server_ip>\n", argv[0]);
        return EXIT_FAILURE;
    }

    const char *filename = argv[1];
    const char *server_ip = argv[2];
    int sockfd = -1;
    int fd = -1;
    char *buffer = NULL;
    pthread_t threads[THREAD_COUNT];
    ThreadData thread_data[THREAD_COUNT];

    // 创建网络套接字
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket creation failed");
        return EXIT_FAILURE;
    }

    // 设定服务器地址
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) {
        perror("invalid address/ address not supported");
        close(sockfd);
        return EXIT_FAILURE;
    }

    // 连接到服务器
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("Connection failed");
        close(sockfd);
        return EXIT_FAILURE;
    }

    // 打开文件并获取大小
    if ((fd = open(filename, O_RDONLY)) < 0) {
        perror("open failed");
        close(sockfd);
        return EXIT_FAILURE;
    }

    off_t file_size = lseek(fd, 0, SEEK_END);
    if (file_size < 0) {
        perror("lseek failed");
        close(fd);
        close(sockfd);
        return EXIT_FAILURE;
    }
    lseek(fd, 0, SEEK_SET);

    // 打印原文件大小
    printf("原文件大小: %ld 字节\n", file_size);

    // 分配缓冲区
    buffer = (char *)malloc(file_size);
    if (buffer == NULL) {
        perror("malloc failed");
        close(fd);
        close(sockfd);
        return EXIT_FAILURE;
    }

    // 读取文件内容
    ssize_t bytes_read = read(fd, buffer, file_size);
    if (bytes_read != file_size) {
        perror("read failed");
        free(buffer);
        close(fd);
        close(sockfd);
        return EXIT_FAILURE;
    }

    // 计算文件的 MD5 值
    unsigned char md5[MD5_DIGEST_LENGTH];
    MD5((unsigned char *)buffer, file_size, md5);

    // 发送文件名长度
    size_t filename_len = strlen(filename);
    if (safe_send(sockfd, &filename_len, sizeof(filename_len), 0) < 0) {
        perror("send filename length failed");
        free(buffer);
        close(fd);
        close(sockfd);
        return EXIT_FAILURE;
    }

    // 发送文件名
    if (safe_send(sockfd, filename, filename_len, 0) < 0) {
        perror("send filename failed");
        free(buffer);
        close(fd);
        close(sockfd);
        return EXIT_FAILURE;
    }

    // 发送文件大小
    if (safe_send(sockfd, &file_size, sizeof(file_size), 0) < 0) {
        perror("send file size failed");
        free(buffer);
        close(fd);
        close(sockfd);
        return EXIT_FAILURE;
    }

    // 发送 MD5 值
    if (safe_send(sockfd, md5, MD5_DIGEST_LENGTH, 0) < 0) {
        perror("send MD5 failed");
        free(buffer);
        close(fd);
        close(sockfd);
        return EXIT_FAILURE;
    }

    struct timeval start_time, end_time; 
    gettimeofday(&start_time, NULL); 

    // 初始化互斥锁和条件变量
    if (pthread_mutex_init(&send_mutex, NULL) != 0) {
        perror("mutex init failed");
        free(buffer);
        close(fd);
        close(sockfd);
        return EXIT_FAILURE;
    }
    if (pthread_cond_init(&cond_var, NULL) != 0) {
        perror("cond var init failed");
        free(buffer);
        close(fd);
        close(sockfd);
        pthread_mutex_destroy(&send_mutex);
        return EXIT_FAILURE;
    }

    // 创建线程，逐块发送数据
    for (int i = 0; i < THREAD_COUNT; i++) {
        thread_data[i].sockfd = sockfd;
        thread_data[i].thread_id = i;
        thread_data[i].offset = i * (file_size / THREAD_COUNT);
        thread_data[i].size = (i == THREAD_COUNT - 1) ? (file_size - thread_data[i].offset) : (file_size / THREAD_COUNT);
        thread_data[i].data = buffer + thread_data[i].offset;

        // 打印调试信息，确认数据块划分是否正确
        printf("Thread %d: Offset %ld, Size %zu\n", i, thread_data[i].offset, thread_data[i].size);

        if (pthread_create(&threads[i], NULL, send_data, (void *)&thread_data[i]) != 0) {
            perror("pthread_create failed");
            for (int j = 0; j < i; j++) {
                pthread_cancel(threads[j]);
            }
            free(buffer);
            close(fd);
            close(sockfd);
            pthread_mutex_destroy(&send_mutex);
            pthread_cond_destroy(&cond_var);
            return EXIT_FAILURE;
        }
    }

    // 等待所有线程完成
    for (int i = 0; i < THREAD_COUNT; i++) {
        if (pthread_join(threads[i], NULL) != 0) {
            perror("pthread_join failed");
        }
    }

    // 记录结束时间
    gettimeofday(&end_time, NULL); 

    // 计算传输时间
    double time_taken = (end_time.tv_sec - start_time.tv_sec) + 
                        (end_time.tv_usec - start_time.tv_usec) / 1000000.0; 

    // 计算传输速率
    double transfer_rate = (file_size / (1024.0 * 1024.0)) / time_taken; 

    printf("\n文件传输完成。\n");
    printf("传输时间: %.2f 秒\n", time_taken);
    printf("传输速率: %.2f MB/s\n", transfer_rate);

    // 销毁互斥锁和条件变量
    if (pthread_mutex_destroy(&send_mutex) != 0) {
        perror("mutex destroy failed");
    }
    if (pthread_cond_destroy(&cond_var) != 0) {
        perror("cond var destroy failed");
    }

    // 清理
    free(buffer);
    close(fd);
    close(sockfd);

    return 0;
}